// ==================================================================================
// Shared Genomics Project MPI Codebase
// Version 1.0 30/04/2010
//
// (c) 2010 University of Manchester all rights reserved
//
// This file is distributed under the GNU General Public License, Version 2.  
// Please see the file COPYING.txt for more details
// ==================================================================================

/*!
\file
\brief Implementation of SNP List for MPI Cores
*/

#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "bmapfile.h"
#include "copyfile.h"
#include "locus.h"
#include "suffixes.h"
#include "utils.h"

int bmapfile_create(struct mapfile *m, struct chromosomes *c, struct selected_options *ops) {
	FILE *out = NULL;
	struct small_locus loc;
	int i;

	if (m == NULL || c == NULL || ops == NULL) 
		return 0;
	else if (m->nLocus == 0 || !c->isdefined || strlen(ops->szBmapFileName) == 0)
		return 0;

	out = fopen(ops->szBmapFileName, "wb");
	if (out == NULL) return 0;

	// No. locus from the map file.
	if (fwrite(&(m->nLocus), sizeof(int), 1, out) != 1) {
		if (out != NULL) fclose(out);
		return 0;
	}

	for (i = 0; i < m->nLocus; i++) {
		// Fill the required 'small' locus fields and then write to the file
		strcpy(loc.name, m->locus[i]->name);
		loc.bp = m->locus[i]->bp;
		loc.chr = (short) m->locus[i]->chr;
		loc.A = loc.B = TRUE;
		strcpy(loc.allele1, m->locus[i]->allele1);
		strcpy(loc.allele2, m->locus[i]->allele2);

		if (fwrite(&loc, sizeof(struct small_locus), 1, out) != 1) {
			if (out != NULL) fclose(out);
			return 0;
		}
	}

	if (out != NULL) fclose(out);

	return 1;
}

struct small_locus* bmapfile_load(struct selected_options *ops, int *nSnps) {
	FILE *in = NULL;
	struct small_locus* loci = NULL;
	int snps_read = 0;
	
	if (ops == NULL) return NULL;
	else if (strlen(ops->szBmapFileName) == 0) return NULL;
	
	in = fopen(ops->szBmapFileName, "rb");
	if (in == NULL) return NULL;

	if (fread(nSnps, sizeof(int), 1, in) != 1) {
		if (in != NULL) fclose(in);
		return NULL;
	}

	// Shouldn't be zero samples in the input file.
	if (nSnps <= 0) {
		if (in != NULL) fclose(in);
		return loci;
	}

	// Allocate the SNP array and read from input file.
	loci = (struct small_locus*) malloc(sizeof(struct small_locus) * (*nSnps));
	if (loci != NULL) {
		snps_read = (int) fread(loci, sizeof(struct small_locus), *nSnps, in);
		if (snps_read != (*nSnps)) {
			if (loci != NULL) free(loci);
			*nSnps = 0;
			return NULL;
		}
	}

	if (in != NULL) fclose(in);

	return loci;
}

int bmapfile_read_nSNPs(struct selected_options *ops) {
	FILE *in = NULL;
	int nSNPs = 0;

	if (ops == NULL) return nSNPs;
	else if (strlen(ops->szBmapFileName) == 0) return nSNPs;

	in = fopen(ops->szBmapFileName, "rb");
	if (in != NULL) {
		if (fread(&nSNPs, sizeof(int), 1, in) != 1) nSNPs = 0;
		fclose(in);
	}
	if (nSNPs < 0) nSNPs = 0;

	return nSNPs;
}

struct small_locus* bmapfile_fseek(struct selected_options *ops, int start, int end) {
	struct small_locus *loci = NULL;
	FILE *bin = NULL;
	int nSNPs = 0;
	BOOL jobDone = FALSE;

	if (ops == NULL || start < 0 || end <= 0 || start >= end) return NULL;
	else if (strlen(ops->szBmapFileName) == 0) return NULL;

	bin = fopen(ops->szBmapFileName, "rb");
	if (bin != NULL) {
		if (fread(&nSNPs, sizeof(int), 1, bin) == 1) {
			if (nSNPs > 0 && start >= 0 && end <= nSNPs) {
				int slice_size = abs(end-start);
				long int start_reading_from = sizeof(int) + (sizeof(struct small_locus) * start);

				if (fseek(bin, start_reading_from, SEEK_SET) == 0) {
					loci = (struct small_locus*) malloc(sizeof(struct small_locus) * (slice_size));
					if(loci != NULL) {
						if (fread(loci, sizeof(struct small_locus), slice_size, bin) == (size_t) slice_size) jobDone = TRUE;
					}
				}
			}
		}
		fclose(bin);
	}

	// I/O occurred when doing the read so drop the 'snps' array to flag an error.
	if (!jobDone && loci != NULL) {
		free(loci);
		loci = NULL;
	}

	return loci;
}

int bmapfile_write_filtered(struct small_locus *m, int nSNPs, BOOL *snps_to_del, struct selected_options *ops, char *ldir, char *rdir) {
	FILE *out = NULL;
	char localFilepath[FILE_PATH_LENGTH], remoteFilepath[FILE_PATH_LENGTH];
	BOOL jobDone = FALSE;
	int nSNPs_to_write = 0, nSNPs_wrote = 0, i;

	if (m == NULL || nSNPs <= 0 || snps_to_del == NULL || ops == NULL || ldir == NULL || rdir == NULL) return 0;
	else if (strlen(ops->szRunId) == 0 || strlen(ldir) == 0 || strlen(rdir) == 0) return 0;

	for (i = 0; i < nSNPs; i++) if (!snps_to_del[i]) nSNPs_to_write++;
	if (nSNPs_to_write == 0) return 0;

	sprintf(localFilepath, "%s%s%s", ldir, ops->szRunId, SUFFIX_BMAP_FILE);
	sprintf(remoteFilepath, "%s%s%s", rdir, ops->szRunId, SUFFIX_BMAP_FILE);
	out = fopen(localFilepath, "wb");
	if (out != NULL) {
		if(fwrite(&nSNPs_to_write, sizeof(int), 1, out) == 1) {
			for (i = 0; i < nSNPs; i++) {
				if (snps_to_del[i]) continue;

				if (fwrite(&m[i], sizeof(struct small_locus), 1, out) == 1) nSNPs_wrote++;
				else break;
			}
		}
		fclose(out);
		if (nSNPs_wrote == nSNPs_to_write) jobDone = copyAndDeleteFile(localFilepath, remoteFilepath) ? TRUE : FALSE;
		else deleteFile(localFilepath);
	}

	if (jobDone) return 1;
	else return 0;
}

int bmapfile_create_and_copy(struct mapfile *m, struct chromosomes *c, struct selected_options *ops, int rank) {
	char remoteFilepath[FILE_PATH_LENGTH];
	BOOL jobDone = FALSE;
	
	if (ops == NULL || m == NULL || c == NULL) return 0;

	sprintf(ops->szBmapFileName, "%s%s%s_%i", ops->ldir, ops->szRunId, SUFFIX_BMAP_FILE, rank);
	sprintf(remoteFilepath, "%s%s%s", ops->rdir, ops->szRunId, SUFFIX_BMAP_FILE);
	if(bmapfile_create(m, c, ops)) {
		if (copyAndDeleteFile(ops->szBmapFileName, remoteFilepath)) jobDone = TRUE;
	}

	if (jobDone) return 1;
	else return 0;
}

struct small_locus* _bmapfile_load(struct selected_options *ops, int *nSnps) {
	if (ops == NULL || nSnps == NULL) return NULL;

	sprintf(ops->szBmapFileName, "%s%s%s", ops->rdir, ops->szRunId, SUFFIX_BMAP_FILE);
	return bmapfile_load(ops, nSnps);
}

